home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 37
/
Aminet 37 (2000)(Schatztruhe)[!][Jun 2000].iso
/
Aminet
/
game
/
patch
/
jst32b_dev.lha
/
jst_dev
/
docs
/
engonly
/
jotdstartup.doc
< prev
next >
Wrap
Text File
|
2000-03-31
|
86KB
|
3,005 lines
TABLE OF CONTENTS
JOTDStartup/JOTD HD Startup code autodocs
JOTDStartup/Memory setup
JOTDStartup/Calling the user code
JOTDStartup/Useful macros
JOTDStartup/STORE_REGS-RESTORE_REGS
JOTDStartup/Console output
JOTDStartup/PatchMacros
JOTDStartup/CallMacros
JOTDStartup/Compelling Macros
JOTDStartup/MiscMacros
JOTDStartup/Relocatable Memory operations
JOTDStartup/Registers: CAUTION!
JOTDStartup/JoypadState()
JOTDStartup/GetUserData()
JOTDStartup/GetUserFlags()
JOTDStartup/SetLocalVarZone()
JOTDStartup/SetFilesPath()
JOTDStartup/UseHarryOSEmu()
JOTDStartup/DisableChipmemGap()
JOTDStartup/ReadUserDir()
JOTDStartup/SaveOSData()
JOTDStartup/Reboot()
JOTDStartup/AllocExtMem(), Alloc24BitMem()
JOTDStartup/OpenFakeExec()
JOTDStartup/FreezeAll()
JOTDStartup/LoadDisks()
JOTDStartup/LoadDisksIndex()
JOTDStartup/LoadDiskFromName()
JOTDStartup/LoadSmallFiles()
JOTDStartup/LoadFiles()
JOTDStartup/GetFileLength()
JOTDStartup/TestFile()
JOTDStartup/TestFileAbs()
JOTDStartup/LoadRNCFile()
JOTDStartup/TransfRoutines()
JOTDStartup/BlockFastMem()
JOTDStartup/Degrade()
JOTDStartup/Enhance()
JOTDStartup/DegradeGfx()
JOTDStartup/DegradeCpu()
JOTDStartup/Display()
JOTDStartup/CloseAllQuiet()
JOTDStartup/CloseAll()
JOTDStartup/FlushCachesSys()
JOTDStartup/GetMemFlag()
JOTDStartup/TestxMbChip()
JOTDStartup/CheckAGA()
JOTDStartup/GetSR()
JOTDStartup/GetAttnFlags()
JOTDStartup/WriteFileHD()
JOTDStartup/WriteUserFileHD()
JOTDStartup/ReadUserFileHD()
JOTDStartup/ReadFile()
JOTDStartup/ReadFilePart()
JOTDStartup/ReadFilePartHD()
JOTDStartup/ReadFileHD()
JOTDStartup/DeleteFileHD()
JOTDStartup/DeleteUserFileHD()
JOTDStartup/InGameOSCall()
JOTDStartup/PatchExceptions()
JOTDStartup/FlushCachesHard()
JOTDStartup/GoECS()
JOTDStartup/ResetDisplay()
JOTDStartup/ResetSprites()
JOTDStartup/KickVerTest()
JOTDStartup/Kick37Test()
JOTDStartup/InitTrackDisk()
JOTDStartup/TrackLoad
JOTDStartup/SetTDUnit()
JOTDStartup/ReadFileFast()
JOTDStartup/ReadRobSectors()
JOTDStartup/ReadDiskPart()
JOTDStartup/StrcpyAsm()
JOTDStartup/StrcmpAsm()
JOTDStartup/StrlenAsm()
JOTDStartup/ToUpperAsm()
JOTDStartup/HexReplaceLong()
JOTDStartup/HexReplaceWord()
JOTDStartup/SetFakeFunction()
JOTDStartup/CRC16()
JOTDStartup/HexToString()
JOTDStartup/Save & Restore hardware registers
JOTDStartup/RNCLength()
JOTDStartup/RNCDecrunch()
JOTDStartup/RNCDecrunchEncrypted()
JOTDStartup/ATNDecrunch()
JOTDStartup/PPDecrunch()
JOTDStartup/FungusDecrunch()
JOTDStartup/BlackScreen()
JOTDStartup/EnterDebugger()
JOTDStartup/SetTraceVector()
JOTDStartup/WaitMouse()
JOTDStartup/WaitMouseInterrupt()
JOTDStartup/InGameExit()
JOTDStartup/IsRegistered()
JOTDStartup/SetExitRoutine()
JOTDStartup/InGameIconify()
JOTDStartup/PatchMoveCList_Abs()
JOTDStartup/PatchMoveCList_Ind()
JOTDStartup/PatchMoveCList_Idx()
JOTDStartup/PatchMoveBlit_Idx()
JOTDStartup/StoreCopperPointer()
JOTDStartup/TellCopperPointer()
JOTDStartup/BeamDelay()
JOTDStartup/WaitBlit()
JOTDStartup/SetQuitKey()
JOTDStartup/SetIconifyKey()
JOTDStartup/LogPatch()
JOTDStartup/InitLogPatch()
JOTDStartup/The printlog tool
JOTDStartup/JOTD HD Startup code autodocs
***************************************************************************
JOTD Startup V3.1
© Copyright 1995-99 Jean-François Fabre/Ralf Huvendiek
***************************************************************************
Memory setup
Starting your code
Why decrunch routines ?
Macros
Library Functions
The printlog tool
Contact us
-----------------------------------------------------------------------------
Converted using GuideML V1.6, a converter written by Richard Körber
<shred@chessy.aworld.de>
JOTDStartup/Memory setup
To understand some choices and notions, you have to know how the memory
is organized by my loading routines:
On a fastmem amiga:
V39:
High Fast: User object, General reloc routines, disks images or RAM files,
OS chip mirror, extension mem (for the 1MB games) (if any)
Fast: The JOTDStartup program routines not relocated.
Low Chip: The game (can take 512K, 1024K or 2048K)
Older than V39:
Fast: User object, General reloc routines, disks images or RAM files,
OS chip mirror, extension mem (for the 1MB games) (if any),
the HD load program
Low Chip: The game (can take 512K, 1024K or 2048K)
On a chip only amiga (V39):
High Chip: User object, General reloc routines, disks images or RAM files,
OS chip mirror, extension mem (for the 1MB games) (if any)
Chip: The game, The JOTDStartup program routines not relocated.
Obviously, while the game is running, you cannot call non-relocated routines
(the ones callable with JSRABS), or else it will crash. You won't need
those routines anyway during the game (you can use JSRABS routines in
a code called using InGameOSCall)
If the game needs more than 512K to run, you'll have to find where the expansion
memory detection is done to replace it by a pointer on the extension mem you allocated
with AllocExtMem(), else the game can crash (older than V39)
The NOFAST tooltype will try to allocate chipmem for AllocExtMem unless you've got
no chipmem above $80000 (the assumed chip size before you make a SaveOSData())
In this case it will return 0 (it's useless to allocate mem that will be overwritten by
the game)
Note: If you succeed in tracking the game memory 'allocation', you don't need relocatable
routines, and nothing will overwrite your code,
but it depends on which level of quality you want when you program your installers...
And this is also compulsory if you want to install a clean quit option!
In the case you would like to leave some OS code done by the game, JST supports ExecBase
emulation since V0.7. Some calls are supported, others are redefinable by the user,
(see OpenFakeExec() function)
JOTDStartup/Calling the user code
Your relocatable object file will be loaded by JST and it will be checked
against relocatable symbols. For instance, you cannot do things like:
move.l #20,var
because this instruction needs relocation, and we can't afford it since
JST is not yet able to do this. Instead you can use one of the reloc macros.
This instruction will become:
RELOC_MOVEL #20,var
JST passes some arguments in registers:
D0.L: 0 if no trainer detected via tooltypes/args, -1 if trainer requested
D1.L: 0 if OS swap is allowed, -1 if not (see InGameOsCall())
D2.L: -1 if JOYPAD is set in the tooltypes, 0 else
D3.L: -1 if HDLOAD is set in the tooltypes, 0 else
D4.L: -1 if BUTTONWAIT is set in the tooltypes, 0 else
D5.L: -1 if LOWMEM is set in the tooltypes, 0 else
The others (function tables) are passed in the HD_PARAMS structure.
Use the macros, and don't fool with the variables in HD_PARAMS.
JOTDStartup/Useful macros
Some of these macros are compulsory for a good compilation and linking
of the user program. They are compatible with phxass and should cause no
problem with other assemblers.
STORE_REGS
RESTORE_REGS
Mac_printf
PUTS
NEWLINE
GETUSRADDR
GETGENADDR
PATCHUSRJMP
PATCHUSRJSR
PATCHABSJMP
PATCHABSJSR
PATCHGENJMP
PATCHGENJSR
JSRGEN
JSRGEN_FREEZE
JMPGEN
JSRABS
JMPABS
TESTFILE
WAIT_BLIT
BEAM_DELAY
GETLVO
WAIT_JOY
WAIT_LMB
HDPARAMS
SAVEOS_DATA
SET_VARZONE
RELOC_MOVEL
RELOC_MOVEW
RELOC_MOVEB
RELOC_CLRL
RELOC_CLRW
RELOC_CLRB
RELOC_STL
RELOC_STW
RELOC_STB
RELOC_TSTL
RELOC_TSTW
RELOC_TSTB
RELOC_SUBL
RELOC_SUBW
RELOC_SUBB
RELOC_ADDL
RELOC_ADDW
RELOC_ADDB
Using registers
JOTDStartup/STORE_REGS-RESTORE_REGS
STORE_REGS
Save all the registers to stack (D0 to A6)
RESTORE_REGS
Restore all the registers from stack (D0 to A6)
No parametrers expected.
JOTDStartup/Console output
Mac_printf:
Prints messages with an easy syntax in an assembly program. Avoids label definition,
and all the problems going with it.
Mac_printf "Hello world"
Mac_printf "You are ",jkjdkfj
Mac_printf "a hd-installer maniac"
will display on the console:
Hello world
You are a hd-installer maniac
If you use 2 arguments in the macro, there will be no linefeed.
PUTS:
Puts a string on the screen
Argument: string pointer.
NEWLINE:
Just skips a line. No argument needed.
Note: Those macros use the Display() routine.
JOTDStartup/PatchMacros
GETUSRADDR
Returns in D0 the relocated address of a routine in the user code.
This avoids a lea addr(pc),A0 then move.l A0,D0, but is not really useful anymore in
the JST environnement.
GETGENADDR
Returns (in D0) the address of a relocated general purpose routine.
Example:
lea $1456(A1),A0
GETGENADDR ReadRobSectors
move.w #$4EF9,(A0)+
move.l D0,(A0)+
PATCHUSRJMP
Sets a JMP to a given user relocatable address. The patched location is absolute.
It may be used for chipmem, but not for extension mem, since you cannot know the
absolute address (you have to calculate it). Do it by hand in this case.
PATCHUSRJSR
Same thing, but with a JSR
PATCHABSJMP
PATCHABSJSR
Obsolete. Same thing as PATCHUSRJMP/JSR
PATCHGENJMP
Sets a JMP in the general purpose relocated area (i.e. the functions of this
library callable with JSRGEN)
PATCHGENJSR
Same thing, for JSRs
JOTDStartup/CallMacros
JSRGEN
JSRGEN_FREEZE
JMPGEN
Calls a relocated general purpose function.
E.g: JSRGEN TrackLoad
Those macros use A5 to get the function address. This register is restored.
JSRGEN_FREEZE can be useful if A5 (and also other registers used by the JST function)
is used during interrupts.
This macro freezes all the interrupts by setting SR to $2700 (means that
you've got to be in superuser mode to use it), executes
the function, then restores SR. I had to use it in Menace and Lotus Turbo Challenge.
JSRABS
JMPABS
Calls a absolute general purpose subroutine
E.g: JSRABS LoadDisks
JOTDStartup/Compelling Macros
Those macros are used in (almost) every loader.
GO_SUPERVISOR
Calls the Supervisor exec routine and stores the userstack in a variable.No argument needed.
Since v1.1d GO_SUPERVISOR can be called twice safely (there's a check for user mode)
SAVE_OSDATA
Calls SaveOSData() with the chipmem you want to be saved.
If you want to save 512K chipmem, type
SAVE_OSDATA $80000
Passing $200000 to the function will allocate as much as chipmem possible, and
mirror only the other parts (0-low boundary and high boundary-$200000)
If you want to save all the 2MB of chipmem:
SAVE_OSDATA $200000
This will allocate a big block of chipmem, and this block won't have to be mirrored.
Passing 2 arguments to SAVE_OSDATA will activate autoquit key feature.
The 2nd argument is the autoquit key that you want to use (raw keycode)
Passing 3 arguments to SAVE_OSDATA will allow you to specify some custom user code
you want called on exit (just like in SetExitRoutine())
The 3rd argument is the address of the custom routine.
HD_PARAMS
A compulsory macro which declares some variables, and also set tags for JST to read
and write data and also recognize the loader.
HD_PARAMS "game.d",filesize,number of disks
The filesize can be set to STD_DISK_SIZE (901120) for normal trackload copiable disks.
See the examples.
In diskfile mode, this call makes JST expect the diskfiles to be named game.d1, game.d2, etc...
In files mode, the first argument allows to specify the subdir where the data files are
to be located. (But you can also use SetFilesPath() to set the subdir too)
For a loader using files located in the same directory as the loader:
HD_PARAMS "",0,0
WARNING: This macro has 3 arguments. It's not possible to choose the window name any more.
An error message will show up when assembling if you put the wrong number of arguments.
SET_VARZONE:
A macro useful in the case you want to add snapshot option to your loader and you've got local
variables that you need to save in the loader (e.g. current disk unit, some flags...)
Put it everywhere you want. The best thing is to put it right after HD_PARAMS macro.
The code will be executed even before your loader code.
This macro consists in a simple JSRABS SetLocalVarZone, with in A0 the start location of the
variables (argument 1 of the macro) and in A1 the end location of the variables (argument 2
of the macro).
All registers are preserved.
If you pass invalid values to this routine (e.g start after end) JST will tell it and quit.
JOTDStartup/MiscMacros
WAIT_LMB:
A simple left mouse button pause (port 0).
WAIT_JOY:
A simple joystick button pause (port 1).
TESTFILE:
Calls the TestFile() function, with relocatable argument.
TESTFILE fname
tst.l D0
bne FileError
Actually this routine takes D0 as input and that's a bit annoying since in a relocatable
environment we have to lea (pc) in an address register then move to D0 and call the routine.
I don't want to change the way the routine works (backwards compatibility)
but this little macro makes life easier.
GETLVO:
A simple macro to get LVO offset in D0.
Example: GETLVO AvailMem will return negative offset of AvailMem
Intended to use with SetFakeFunction()
WAIT_BLIT
A macro to wait for the blitter operation to end (equivalent to WaitBlit
but does not use any registers, useful for games which use/modify registers
in interrupts)
BEAM_DELAY
A macro to wait using the vertical beam. Takes one argument.
BEAM_DELAY 1
will wait approx. 20 ms (PAL)
JOTDStartup/Relocatable Memory operations
RELOC_MOVEL/W/B:
A macro which allows to move some data in an address specified by a label, but in a relocatable
way:
move.l D0,label ; is not relocatable and JST will refuse it
move.l A6,-(sp)
lea label(pc),A6
move.l D0,(A6) ; is relocatable. but a pain to write
move.l (sp)+,A6
That's what RELOC_MOVEL does.
For Word and Byte format, similar macros known as RELOC_MOVEW and RELOC_MOVEB exist.
RELOC_TSTL/W/B:
With the 68020+ instruction set, it's possible to test a label in a relocatable way:
tst.l label(pc)
But with the 68000 this is not possible. So I made a useful macro:
RELOC_TSTL label
RELOC_CLRL/W/B:
Makes a RELOC_MOVE/W/B #0 to location
RELOC_ADDL/W/B:
RELOC_SUBL/W/B:
relocatable add/sub
JOTDStartup/Registers: CAUTION!
Be careful not to use registers A5 and A6 with some macros, because they
often use those registers: e.g:
PATCHUSRJMP ($30,A5),PatchLoader
will crash because PATCHUSRJMP uses A5. This is subject to change, though.
To change working registers (A5,A6) used in the macros to lets say (A3,A4),
just include the following lines BEFORE the jst.i include.
REDEFINED_REGISTERS = 1 ; to tell jst.i that we've redefined the registers
Ax EQUR A3 ; work register #1
Ay EQUR A4 ; work register #2
Or manually:
move.l A0,-(sp)
move.l A5,A0
PATCHUSRJMP ($30,A0),PatchLoader
move.l (sp)+,A0
JOTDStartup/JoypadState()
ULONG JoypadState(port)
D0 D0
This routine allows to check CD32 joypad buttons. It obviously does not use
lowlevel.library and is buggy unless on a CD32 (why???). It's ripped from
James Pond 3 routine (I was not the first since the ReadJoypad.s from aminet
was also ripped from this one).
port can be 0 (mouse port) or 1 (joystick port).
The routine returns bits that you can test. The bits are defined in gp_macros.i
moveq #0,D0
JSRGEN JoypadState
btst #AFB_START ; start/pause button
beq nostart$
...
On a normal amiga, you've got to go in a direction to make this routine work.
I don't know why, but it works perfectly on a CD32.
Also, you can't press fire (red button) and the other buttons together, or only fire
will be detected.
You have to wait for a vertical blank to occur before you can use this function.
I suggest that you put it in the level 3 interrupt, just before acknowledge
(which is a move in $DFF09C) and set the flags here. Then another routine will
check those flags and emulate game keys such as pause, jump/fire, etc... it's
up to you :)
NOTE:
If you only want to check the second button (blue, or Sega joystick 2nd button),
you can also read $DFF016 and check for bit 14 low, still after a VBLANK.
Don't forget to reset the potgo in that case by
move.w #$CC01,($DFF034)
See also:
JOTDStartup/GetUserData()
APTR GetUserData(void)
A0
This routine returns in A0 a pointer to the NULL terminated string passed by
the USERDATA argument or tooltype. It points to an empty string if USERDATA
was not filled. It's useful to set a swith other than the user ones already present
such as TRAINER, JOYPAD... and some info can be passed (it's not only a switch)
I used it in the Awesome JST ripper to specify the disk, side and drive unit.
See also: GetUserFlags()
JOTDStartup/GetUserFlags()
ULONG GetUserFlags(void) - returns loader set options
D0
This routine returns in D0 a combination of the option the user could have set.
Not all the options are present here, only some switches which are:
TRAINER
HDLOAD
LOWMEM
NTSC
NOOSSWAP
JOYPAD
To test if an option is active, just do a btst #AFB_<option>,D0 after having called
GetUserFlags(). Example:
JSRGEN GetUserFlags
btst #AFB_NTSC,D0
bne .nontsc
; ntsc set
; do stuff for NTSC ...
.nontsc
See also: GetUserData()
JOTDStartup/SetLocalVarZone()
void SetLocalVarZone(start,end) - Sets saveable variable location in the loader
A0 A1
When you make a snapshot, you save JST internal memory and the game internal memory,
but you may also need to save some data relevant to your loader (e.g. current disk).
You may not need this routine if your loader is completely state-independent
(i.e. does not require variables to work)
Extension memory pointer does not count as it's restored by the snapshot.
No need to save this one.
Always call this routine before SAVE_OSDATA (or use the SET_VARZONE macro)
See also: InGameIconify, SET_VARZONE (macro)
JOTDStartup/SetFilesPath()
void SetFilesPath(dirname) - Changes default path for files to load to wished value
A0
The first argument of HD_PARAMS can no longer be the subdir where the data files are
to be loaded.
Use SetFilesPath instead. This function will change the load directory.
See also:
JOTDStartup/UseHarryOSEmu()
void UseHarryOSEmu(void) - allows the use of Harry's excellent OS emulation program
This function will allow you to use Harry's OS emulation program.
This program must be called OSEmu.400 and be located in the C: directory.
This function allows a very powerful emulation, actually much better and complete
than my OpenFakeExec() function!!
Read the documentation in the source code of OSEmu.asm for more details and refer
to the JST loaders examples.
QUITKEY tooltype allows to change the default quit key (which is '*' of the numerical
keyboard, provided that the keyboard is US/German. Actually this key is the upper right
key of the numeric keypad, sorry A600 users :) )
See also: OpenFakeExec()
JOTDStartup/DisableChipmemGap()
void DisableChipmemGap(void) - deactivates gap in memory shadowing
When you call the macro SAVEOS_DATA with a maxchip value as argument,
let's say for instance SAVEOS_DATA $80000, JST will allocate $80000 bytes
of fastmem or chipmem (address above $80000 at all rates) to save the
chipmem between $0 and $80000, and to swap it when the loader quits or
swaps between game and OS (read/write data from HD...)
If the maxchip value is above $1FE000, JST does not normally allocate 2
megabytes of fast memory because it's useless to waste that much memory,
and uses AllocMem() on chipmem to allocate the largest chipmem block possible.
This block being allocated, there's no need to save all the 2MB of memory but
just below the lower bound of the allocated block and above the upper bound
of the allocated block.
But there's a technical problem here: when the loader reads/writes from the HD,
it's rather hard to know where the data will be written/read from, because
there's a gap which is not "shadowed", so I made this routine which forces JST
to allocate 2 megabytes to save the chipmem, without the gap trick.
It costs more memory, but allows proper OS swap and data read/write.
This routine has no effect (at the moment) if the argument passed to SAVEOS_DATA
is below $1FE000.
See also: SaveOSData()
JOTDStartup/ReadUserDir()
ULONG ReadUserDir(dirname, buffer, maxentries, maxnamelen) - Reads dir from the user dir
D0 A0 A1 D0 D1
This function will read the files in the directory specified in A0. The SAVEDIR value
will be concatenated in the path if SAVEDIR is provided in the tooltypes (OS 2.0+ only)
The filenames will be copied in the output buffer specified in A1.
Please note that only the plain files will be copied. Directories will be ignored.
This function will return the number of plainfiles in the directory in D0.
maxentries will allow to limit the number of files to be copied. It will prevent a buffer
overflow. Set it to 0 if you don't want any limit.
maxnamelen will allow to limit the filename length. Set it to 0 and the limit will be 20
characters.
The filenames will be NULL terminated in the buffer, and spaced by maxnamelen bytes, even
if the filenames are not that long, to allow easy search in the buffer.
e.g set maxnamelen to 32 ($20), and call ReadUserDir. The buffer will look like:
$00: f i r s t _ f i l e0 *rubbish*
$20: s e c o n d _ f i l e0 *rubbish*
$40: t h i r d _ f i l e0 *rubbish*
and so on...
I found it more convenient than a buffer depending on the actual length of the filenames,
because a simple MULU allows to access any filename.
See also: InGameOSCall()
JOTDStartup/SaveOSData()
void SaveOSData(chipmem_size,memsave) - Saves chipmem and custom & cia registers...
D0 D1
Usually called by the SAVE_OSDATA macro.
The most important routine of JST package. You've got to understand it
fully if you want to make a clean loader.
Useful for debug purposes and to be able to quit the game and make in-game
OS calls (read/write files)
The memsave parameter is only used when the chipmem_size is $200000
(AGA game loaded). If set to 0, SaveOSData will alloc the bigger chipmem
chunk as possible and will therefore not care about this part of memory,
so the save buffer in fastmem will be a lot smaller than 2MB.
If set to -1, all chipmem will be saved. If DEBUG is set, memsave
will also be set to -1 and you'll need more memory.
To save 1024K of chipmem:
SAVE_OSDATA $100000
To save 2048K of chipmem, trying to reduce fast memory consumption:
SAVE_OSDATA $200000
If no memory is available, the chipmem won't be saved, and it won't be possible
to quit the game/to get debug information.
As a side effect, this function disables all interrupts/DMA this way:
lea #$DFF000
move.w #$4000,intena(A6)
move.w #$7FFF,intreq(A6)
move.w #$4020,dmacon(A6) ; also remove sprite DMA
This is necessary as the system could keep on modifying and restoring chipmem could
crash the machine. If your game bootup does not like the interrupts to be disabled,
reactivate necessary interrupts/dma yourself.
SAVING MEMORY FOR A 2MB CHIP GAME
(If you can avoid to use the full 2MB of chip do it)
Trying to save 2MB of chip will activate the memsave option.
This option consists in allocating as much as chipmem as possible, to avoid
to allocate a whole block of 2MB fastmem. The allocated chipmem can be trashed
by the game, it will not corrupt the system.
Be careful when you read or write a file to hard disk, because data may not match
(addresses are translated to the fastmem chip mirror during an OS swap
and the gap caused by the memsave option can 'corrupt' data).
If you plan to activate memsave and read/save files to HD, do the disk IO
in a fastmem buffer rather than in chipmem, and transfer it later (that's
why you can read the HDLOAD/LOWMEM flag in the user code)
With diskfiles, check and display an error message if LOWMEM is on, because of
the same problem, but you can't be sure if the bigger load part the game can
do at a time...
See also: GO_SUPERVISOR, InGameExit, InGameOSCall, ReadFileHD, WriteFileHD
JOTDStartup/Reboot()
Just reboots the computer (the OS must be active)
JOTDStartup/AllocExtMem(), Alloc24BitMem()
ULONG AllocExtMem(bytesize) - Allocates memory for expansion memory
ULONG Alloc24BitMem(bytesize) - Allocates memory for expansion memory in 24 bit space
D0 D0
Lots of games need more than the standard 512K of chip memory.
They often do some tests to know where they can write (see the patch section
in the guide).
Sometimes it's better not to leave the game detect the expansion RAM itself.
Using AllocExtMem, you'll allocate a block of 512K, 1024K or any size, and
you'll keep it in mind until the game searches for memory. Patch the routine
with the pointer you allocated and:
1) You'll be sure the game uses fast memory
2) The quit option will be safe, which means the memory will not be corrupted
by the game.
You could do this allocation by hand but as you always quit by calling InGameExit(),
you could not get control again to free the memory.
AllocExtMem() stores the data useful for FreeMem, and InGameExit() tries to free
the memory you allocated automatically.
If you select the NOFAST tooltype, the extension memory block to be returned
will ALWAYS be $80000, and more chip memory will be saved when you call
SaveOSData (no need to add). E.g:
move.l #$80000,D0
JSRABS AllocExtMem
RELOC_MOVEL D0,ExtBase ; relocatable macro for move.l
...
SAVE_OSDATA $80000
WARNING: Do not call this routine more than once, as the first allocated zone won't
be freeable.
Alloc24BitMem is exactly the same function except that it will try to allocate memory
with the 24BITDMA flag set (chip of 24 bit fastmem if you've got some). Useful for
some games that don't like 32 bit fastmem (shitty coded).
Both will check that the beginning of the allocated block is above address $80000,
and will return 0 if it's below.
See also:
JOTDStartup/OpenFakeExec()
ULONG OpenFakeExec(void) - Create a exec-like function table
D0
This routine (new since V0.7 of JST) allocates $300 bytes and creates a function
table that matches ExecBase offsets. Of course all calls are not emulated, and some
are forbidden. For instance, you cannot OpenLibrary() (of course), or OpenDevice
with device name different from "trackdisk.device".
To use only after SaveOSData. Fake ExecBase will be in $4.
For the moment it does not support ExecBase structure. Only functions are supported.
This function table saves a lot of hassle because you don't have to insert BRA.B
everywhere to skip those function calls.
Calls supported:
CacheClearU (flushes caches)
CacheClearE (flushes caches)
CacheControl (flushes caches, returns 0 in D0 and D1)
AllocAbs (returns the value in A1 in D0, as if absblock had been allocated)
DoIO (calls TrackLoad(), as only trackdisk.device can be opened. Returns 0)
OpenDevice (checks for "trackdisk.device", and then returns 0)
FindTask (returns 0 in D0!!)
DoIO() is limited to commands 2 and 9. Write is not supported. If you want it to
be, you have to overwrite the DoIO function with SetFakeFunction().
Inactivated calls:
Disable
Enable
Forbid
Permit
SuperState
UserState
AddPort
RemPort
CloseLibrary
CloseDevice
Calling any of the above will only do a RTS
Unsupported calls:
OpenLibrary
OldOpenLibrary
AllocMem
FreeMem
AvailMem
Calling unsupported calls and other calls will trigger an runtime error.
The difference is that calling an unsupported call will display a specific message
(AllocMem not defined...) whereas other calls are not differentiated in the error
message (sorry you have to hunt for the offset)
Anyway, you can overwrite the system calls with the SetFakeFunction() call.
This way, you can define your own AllocMem(), etc...
If you succeed in defining some useful and cool routines, I'll be glad to integrate
them in JST default fakeexec emulation.
Return Values:
D0: 0 in any cases at the moment, but soon will return 0 if OK.
Note:
If DEADLY tooltype is on (DEADLY sets $FF000001 in ExecBase location to detect games
which use system calls besides other things), OpenFakeExec will display a warning.
DEADLY ExecBase kill will be deactivated.
Sometimes the game just reads in ExecBase to see the first block of fastmem, does calculations
on it with ANDs #$FFF80000 and so on. In this case you have to patch the routine by hand.
Just run the game from floppy or without OpenFakeExec or DEADLY to understand how it behaves,
and then copy the behaviour.
The best thing to do is to have a stock A500 with Action Replay. Then you can see exactly how
the game understands and stores the info for 512K chip and 512K fast.
See Also: SetFakeFunction(),TrackLoad()
JOTDStartup/FreezeAll()
void FreezeAll() - Freeze DMA and interrupts
Be sure to save the custom registers before with SaveCustomRegs.
This function is useful to start/stop a game without being annoyed
by ongoing DMA on interrupts.
See also: SaveCustomRegs(),RestoreCustomRegs()
JOTDStartup/LoadDisks()
void LoadDisks() - Load Disks in memory
Loads the disk from the HD_PARAMS macro parameters in the user program
no parameters are returned. The routine will exit with an error message
if something fails (no memory, file not found...)
else, it will return to the caller.
See also: LoadFiles(), LoadDiskFromName()
JOTDStartup/LoadDisksIndex()
void LoadDisksIndex(index) - Load Disks in memory, starting from index
D0
Same thing as LoadDisks(), but it begins at the disk number specified in D0
See also: LoadDisks(), LoadDiskFromName()
JOTDStartup/LoadDiskFromName()
void LoadDiskFromName(file name) - Load the file as a disk file
D0
Loads the disk from the HD_PARAMS macro parameters in the user program,
but the name will be ignored and replaced by the one you mention.
no parameters are returned. The routine will exit with an error message
if something fails (no memory, file not found...)
else, it will return to the caller.
If you call it twice, first file will be disk 0 and second will be disk 1.
See also: LoadDisks(), LoadFiles()
JOTDStartup/LoadSmallFiles()
void LoadSmallFiles(size_limit) - Load small files in the current directory
D0
This function will load all the files which length is inferior to
size_limit. The other ones will be loaded during game, but it is
transparent to the programmer because the ReadFile() function will
read either from preloaded files or from hard disk if the file is
too big to have been cached.
You'll understand that you'll have to adjust properly size_limit
depending on the game you're patching. If a game directory holds
30 files of 1000 bytes each, and some big files (>50Ko)
you should set size_limit over 1000 else the ReadFileHD will be
called very often and as OS swaps take time and blackout the display,
your loader won't use properly the HDLOAD feature.
NOTE: If HDLOAD is not set, this function will behave exactly as
LoadFiles(), and will read all the files (no size limit)
See also: LoadDisks(), LoadFiles(), ReadFile()
JOTDStartup/LoadFiles()
void LoadFiles() - Load all the files in the current directory
This function will perform a Lock() in the current directory, then will
create the structures to store the files and read them all in memory.
It does not load subdirectories. Only flat files will be read, so be
careful with games storing data in subdirectories. The installation
procedure will have to flatten the file structure.
This function will exit with an error message if an error occurs
else, it will return to the caller
See also: LoadSmallFiles(),LoadDisks(),ReadFileFast(),LoadRNCFile().
JOTDStartup/GetFileLength()
ULONG GetFileLength(filename) - Gets the length (bytes) of a file
D0 D0
This function returns the length of a file in bytes. If the file is not found,
it will return -1, so be careful to test the value.
This function can only be called when the OS is active (not during game,
or if you must do so, do it within a InGameOSCall).
See also: ReadFile()
JOTDStartup/TestFile()
ULONG TestFile(filename) - Tests if a file exists on the hard drive
D0 D0
This function returns 0 if the specified filename exists (relative path
from JST path)
See also: TestFileAbs
JOTDStartup/TestFileAbs()
ULONG TestFileAbs(filename) - Tests if a file exists on the hard drive
D0 D0
This function returns 0 if the specified filename exists (absolute path)
See also: TestFileAbs
JOTDStartup/LoadRNCFile()
ULONG LoadRNCFile(filename) - Loads and decrunches a RNC file from the Hard Drive.
D0/A0/D1 D0
This function loads a file and decrunches it, allocating memory at the same time for the
decrunched length.
Return values: D0: error flag (0 if OK)
A0: beginning of allocated buffer
D1: allocated length (useful if the file space must be freed with FreeMem())
See also - RNCDecrunch(), RNCLength()
JOTDStartup/TransfRoutines()
void TransfRoutines(void) - (obsolete)
This routine is obsolete and provided only for compatibility reasons.
It does strictly nothing.
See also:
JOTDStartup/BlockFastMem()
void BlockFastMem(sparemem) - Blocks fastmem
D0
D0 is the number of bytes to leave unallocated (roughly, not accurate).
0 in D0 means the routine will try to allocate all fast memory available.
This is useful for some games which want chipmem but forget to specify it
in AllocMem() (old games), and also for games which do not like memory
located over $FFFFFF (coded in 32 bits).
Do not use it if not necessary (impossible to return to the OS
after that). You can use it when the TUDE NOFASTMEMORY BOOT=HIGHCHIP
is necessary to boot the wanted game from floppy without problems
TUDE is copyright N.O.M.A.D 1995. It can be found on aminet.
Normally if you track the game memory allocation, you should not use this
routine. Force the game to allocate its extension memory block in chipmem
(provided you've got 1MB chip, see BodyBlows patch).
See also: Degrade(), DegradeCpu().
JOTDStartup/Degrade()
void Degrade(cacheset,cachemask) - Degrades everything to run games properly
D0 D1
This routine is a combination of both DegradeCpu() and DegradeGfx() routines.
Call it just before saving OS data and booting the game.
A classic sequence is:
moveq #0,D0
move.l #CACRF_CopyBack,D1
JSRABS Degrade ; leave only instruction cache
GO_SUPERVISOR
SAVE_OSDATA $80000
.. boot stuff
See also: BlockFastMem(), DegradeCpu(), DegradeGfx(), Enhance()
JOTDStartup/Enhance()
Resets system enhancements (VBR, caches, gfx...)
This function should *NEVER* be called when using the library, but it could be
useful to call it to see what happens in an OS swap that fails. It's a function
that can be used in the standalone degrade code I've written
See also: Degrade()
JOTDStartup/DegradeGfx()
void DegradeGfx() - Degrades graphics to run games properly
This routine degrades the display using LoadView(NULL), and can set PAL or NTSC (tooltype).
It also opens an intuition screen if possible.
See also: BlockFastMem(), DegradeCpu(), Degrade()
JOTDStartup/DegradeCpu()
DegradeCpu(cacheset,cachemask) - Degrades only cpu related hardware
D0 D1
This routine does the following:
- It sets the VBR to $0 for 68010 and higher cpus unless the LEAVEVBR tooltype is present.
- It removes the specific 68060 caches (branch, writeallocate, and also the data/copyback
cache, this last one the CacheControl() function leaves alone, even if you want to disable
CopyBack)
- If NOCACHES is activated in the tooltypes, it will disable ALL caches.
The cacheset and cachemask variables are the same as in exec CacheControl().
Refer to the CacheControl autodoc for more details.
This function does not affect graphics modes at all.
See also: Degrade(), DegradeGfx()
JOTDStartup/Display()
void Display(message pointer) - Displays a message in the opened console or CLI
A1
This function is generally called from the Mac_printf macro. This is more convenient,
but we may need to call it directly in some special cases.
If the program has been run from CLI, Display() will write in the CLI. If run from
workbench, it will write in the window opened by the startup program (see HD_PARAMS macro).
If no window exists, this function will do nothing (it won't even crash).
JOTDStartup/CloseAllQuiet()
void CloseAllQuiet(void) - Frees everything and exits
This function frees all the memory allocated by the program (diskfiles, files, relocatable
code, windows) and quits the program. If you allocated memory from the user program
by using directly Exec routines, you'll have to free it before calling this function.
This function cannot be called when the game is running (the OS is killed). Call InGameExit
instead.
This function will exit at once, and the window will be closed if the program was started
from Workbench.
See also: InGameExit(), CloseAllQuiet()
JOTDStartup/CloseAll()
void CloseAll(void) - Frees everything and exits because of an error
This function frees all the memory allocated by the program (diskfiles, files, relocatable
code, windows) and quits the program. If you allocated memory from the user program, you'll
have to free it before calling this function.
This function cannot be called when the game is running (the OS is killed). Call InGameExit
instead.
If the program has been run from workbench, it will ask for the RETURN key before closing
the console, in order for you to see what went wrong.
See also: InGameExit(), CloseAllQuiet()
JOTDStartup/FlushCachesSys()
void FlushCachesSys(void) - Flushes the caches using OS calls
Well, this function calls CacheControl(0L,0L), and the caches are flushed.
See also: FlushCachesHard(),Degrade()
JOTDStartup/GetMemFlag()
ULONG GetMemFlag(void) - Return MEMF_REVERSE if kick 39+
D1
As the MEMF_REVERSE flag (AllocMem, AllocVec) does not exist in KS 1.x, and was broken
until V39, this function checks for the kick version, and returns 0 if it is too old.
Else, it will return MEMF_REVERSE.
That will explain that JST loaders will have more chances to work on V39 machines.
JOTDStartup/TestxMbChip()
ULONG Test1MBChip() - Hardware test for 1MB chip memory
ULONG Test2MBChip() - Hardware test for 2MB chip memory
This routine Test1MBChip (resp Test2MBChip) pokes in the location $80000 (resp $100000),
then checks for location $0. If they are the same, then chipmem is 512K
(resp 1024K or 512K) ,else, it's at least 1024K (resp 2048K)
This routine is safe. It calls Disable and restores the altered chip locations.
Returns 0 if chipmem is as required (>1024K resp 2048K), -1 else.
NOTE: To test for 512K chip, use Test1MBChip. To check for 1MB chip, use
Test1MBChip, then Test2MBChip to know if it's only 1024K or 2048K.
I don't think further amigas (if there any) will have more than 2MB of chipmem...
Also notice that some computers have 2MB of chipmem and are not AGA, and
that some so-called AGA games only assume you've got 2MB of chip and can
be run on 2MB chip ECS amigas or even on 512K OCS amigas (but in that last
case you'll have to relocate some code heavily!!)
Example: Jungle Strike AGA (Ocean) runs fine on UAE with 2MB of chipmem.
See also: CheckAGA()
JOTDStartup/CheckAGA()
ULONG CheckAGA(void) - Checks DeniseID to see if the computer supports AGA or not
D0
This function will return 0.L in D0 if AGA is supported, -1.L else.
JOTDStartup/GetSR()
UWORD GetSR() - Returns CPU status register
D0
SR will be copied in D0. No need to be in suprevisor mode to call
this function (or else it has no interest).
See also:
JOTDStartup/GetAttnFlags()
UWORD GetAttnFlags() - Returns CPU AttnFlags while OS is down
D0
At startup, exec's AttnFlags is copied and relocated. So if you need
to know something about the CPU you patch is running on, use this
function. You can test the bits like a normal CPU bit test.
See also:
JOTDStartup/WriteFileHD()
WriteFileHD(command,length,name,buffer) - Writes a file to disk during game
D0,D1 D0(=0) D1 A0 A1
This function is very useful to write some data from disk on demand during
the game for savegame data files or hiscores.
D0.L: command: leave to 0 for the moment
D1.L: length in bytes (be careful!!)
A0: pointer on the name, NULL terminated
A1: source data buffer (either in fastmem or chipmem)
Note: as the game chipmem is transfered to a fastmem area during OS calls,
if you load a file into a chipmem zone, it will be loaded in the fastmem
mirror instead, and will appear in chipmem after the OS is killed again,
so don't worry about that (except if you save 2MB chip and activate memsave).
The function returns D0=0 on success, -1 else.
If success, D1.L contains the length actually written.
You can call this function whether the OS is alive or not.
If you want to write user data (like scores or gamesaves), you'd better use WriteUserFileHD()
because this function uses SAVEDIR parameter to write data to another directory in the
registered version of JST.
See also: InGameOSCall(), WriteUserFileHD().
JOTDStartup/WriteUserFileHD()
WriteUserFileHD(command,length,name,buffer) - Writes a file to disk during game in SAVEDIR
D0,D1 D0(=0) D1 A0 A1
Same thing as WriteFileHD, except that if SAVEDIR tooltype is set, (SAVEDIR-RAM:), the data
will be saved here, and not in the game directory. Useful for write only/copyback cached
partitions.
See also: WriteFileHD(), ReadUserFileHD().
JOTDStartup/ReadUserFileHD()
ReadUserFileHD(command,length,name,buffer) - Reads a file to disk during game in SAVEDIR
D0,D1 D0(=0) D1 A0 A1
Same thing as ReadFileHD, except that if SAVEDIR tooltype is set, (SAVEDIR-RAM:), the data
will be saved here, and not in the game directory.
See also: ReadFileHD(), WriteUserFileHD().
JOTDStartup/ReadFile()
ReadFile(command,length,name,buffer) - Reads a file from disk/RAM during game
D0,D1 D0(=0) D1 A0 A1
This command is very similar to ReadFileHD() except that it will
check if the file was not cached in memory first.
In the case HDLOAD/LOWMEM is on, it will often read files from HD. If those flags are off,
all the files will be loaded from memory because they had been preloaded
(by LoadFiles() or LoadSmallFiles()).
See also: ReadFileHD(),ReadFilePart().
JOTDStartup/ReadFilePart()
ReadFilePart(command,length,offset,name,buffer) - Reads a part of a file from disk/RAM during game
D0,D1 D0(=0) D1 D2 A0 A1
This command is very similar to ReadFilePartHD() except that it will
check if the file was not cached in memory first.
In the case HDLOAD/LOWMEM is on, it will often read files from HD. If those flags are off,
all the files will be loaded from memory because they had been preloaded
(by LoadFiles() or LoadSmallFiles()).
See also: ReadFileHD(),ReadFilePartHD().
JOTDStartup/ReadFilePartHD()
ReadFilePartHD(command,length,offset,name,buffer) - Reads a part of a file from disk/RAM during game
D0,D1 D0(=0) D1 D2 A0 A1
This function will try to load a part of a file from HD.
D0: 0 if OK
D1: length read in bytes
See also: ReadFileHD(),ReadFilePart().
JOTDStartup/ReadFileHD()
ReadFileHD(command,length,name,buffer) - Reads a file from disk during game
D0,D1 D0 D1 A0 A1
This function is very useful to read some data from disk on demand during
the game. It can load the game files or savegame data files.
D0: command: 0: normal operation, $FF reserved. Leave to zero at the moment
D1: length in bytes. -1 means all the file
A0: pointer on the name, NULL terminated
A1: destination data buffer (either in fastmem or chipmem)
Note: as the game chipmem is transfered to a fastmem area during OS calls,
if you load a file into a chipmem zone, it will be loaded in the fastmem
mirror instead, and will appear in chipmem after the OS is killed again,
so don't worry about that (except if you save 2MB chip, in that case,
I allocate chipmem to save some fastmem, so there's a gap!!)
The function returns D0=0 on success, -1 else.
If success, D1.L contains the length actually read.
You can call this function whether the OS is alive or not.
This function will not check for cached file in memory. Calling ReadFileHD forces
a read from HD (an OS swap is necessary). If you don't know if the file was cached or
not, use ReadFile() instead.
If you want to read user data (like scores or gamesaves), you'd better use ReadUserFileHD()
because this function uses SAVEDIR parameter to read data from another directory in the
registered version of JST.
See also: ReadFile(), InGameOSCall(), WriteFileHD(), DeleteFileHD(), ReadUserFileHD().
JOTDStartup/DeleteFileHD()
ULONG DeleteFileHD(name) - Deletes a file from disk during game
D0 A0
This can also be done by calling InGameOSCall() but I needed it in the
Sensible Golf patch and then I included this function in the library.
Will return 0 if success, -1 else.
You can call this function whether the OS is alive or not.
If you want to delete user data (like scores or gamesaves), you'd better use DeleteUserFileHD()
because this function uses SAVEDIR parameter to delete data from another directory in the
registered version of JST.
See also: InGameOSCall(), ReadFileHD(), WriteFileHD(), DeleteUserFileHD()
JOTDStartup/DeleteUserFileHD()
ULONG DeleteUserFileHD(name) - Deletes a file from disk during game in SAVEDIR
D0 A0
Same routine as DeleteFileHD(), but uses SAVEDIR if set to search file to delete
into. Unvaluable because cannot be reproduced using InGameOSCall() (because
for the moment it's not possible to get SAVEDIR value from the object)
See also: InGameOSCall(), ReadFileHD(), WriteFileHD(), DeleteUserFileHD()
JOTDStartup/InGameOSCall()
ULONG InGameOSCall(entrypoint) - Calls a routine containing OS code
A4
This is one of the most powerful and also one of the most dangerous function of
this package.
It allows the user to call a program using normal system calls, while the game
has totally destroyed the OS.
It restores the OS and interrupts and saves the game memory, goes in user mode, calls
the user routine, and restores everything for the game.
The display goes black because this is not possible to save the colormap and other
screen data.
The user routine can trash all the registers. They're saved and restored after the call.
However, there are problems with this routine:
- Any Write() made to disk may not terminate when the game is re-activated. So
you'll better disable disk caches and quit the game correctly after that.
If someone knows how to wait for the disk activity to finish, please call me.
The problem that may occur is a devalidation of the disk (not very important).
I use the dos Delay() command after the write operation with 2 seconds, and it works:
move.l #100,D1
move.l _DosBase(pc),A6
JSRLIB Delay
- Playing with the keyboard while the disk is writing or reading locks the system.
I don't know where it comes from, but I'll find out. For the moment, avoid to press
any key during disk accesses (especially writes, this can lead to write errors !!!)
Besides those problems this routine is the only way to make real hard disk versions of games
which load and save data to disk.
Lots of the patches I've written use this routine. It took me some time for it to be reliable.
Some users still have problems. I'm improving it from time to time.
This routine returns 0 when the routine has been called, and -1 if the routine has not been
called because of the impossibility to save/restore the operating system, so be careful for
users without too much memory...
The register D0 you'll set in the OS routine will be passed to your program. It must be coherent
with the error code. E.g: if everything is OK, return 0, else return -1, or -2 to make a difference
with the OS switching error.
Example:
LoadScoreFile:
lea scorename(pc),A0
move.l A0,D1
move.l #MODE_OLDFILE,D2
move.l _SysBase(pc),A6
JSRLIB Open
tst.l D0
beq error$
... ; do our stuff (reading...)
JSRLIB Close
moveq.l #0,D0
rts
error$
moveq.l #-2,D0 ; file error code
rts
LoadHighscores:
lea LoadScoreFile(pc),A4
JSRGEN InGameOSCall
tst.l D0
beq ok$ ; all is OK
cmp.l #-1,D0
beq oserr$ ; os could not be restored
; else file error
I recently added the ReadFileHD() and WriteFileHD() routines, and this
routine is not to be used in most cases, but sometimes it's compulsory
(for instance listing a savegame directory during the game (Cannon Fodder 2))
IMPORTANT: This function will do nothing if the NOOSSWAP tooltype/argument is activated. To check
this argument, the register D1 will be set (-1.L) when _loader is called (beginning of the user
code).
See also: ReadFileHD(), WriteFileHD(), DeleteFileHD(), InGameExit()
JOTDStartup/PatchExceptions()
void PatchExceptions(void) - redirect system exceptions to custom routines
This routine allows to trap the following processor exceptions:
- Bus error
- Address error
- Illegal instruction
- Division by zero
- LINEA emulation
- LINEF emulation
- Format error
- some others... (subject to change)
PatchExceptions() is called at startup, so there's no need to do it by hand
normally, but it may be necessary to call it again in the user program in the
case of the game overwriting the values, and then crashes. That's why this function
is callable from user program.
When an exception is encountered, the handler returns to the OS with the 'exception
occured' or 'linef/movep (060)' message. If you want more information, use the DEBUG
tooltype or argument when running the loader, and a memory image of the game,
the registers an some other information will be written to disk. With the memory
and the stack value, maybe you're able to track the error or find out problems using
memory dump.
See Also:
JOTDStartup/FlushCachesHard()
void FlushCachesHard(void) - Flushes the caches
This routine is *VERY* useful to flush the caches when the OS is overridden
by the game, and you still want to use fast memory with the caches on.
It addresses CACR register to invalidate instruction and data caches,
and it also calls CPUSH to flush the copyback data cache into memory
(for 68040/68060) caches.
NOTE: As long as the OS is alive, use FlushCachesSys() whenever possible.
See also: FlushCachesSys()
JOTDStartup/GoECS()
void GoECS(void) - degrades display and sprites to ECS
This function is very useful when you start the loader from a multiscan screen with
a hires pointer. In that case, if the game uses sprites, they all appear shrinked,
as the game did not switch in lores pointer.
It modifies the value of FMODE (set to 0) and BPLCON3 (set to $0C20).
This acts also on dual playfield parameters.
NOTE: This function will have no effect if the system copperlists are still active,
as they reset BPLCON3 every screen scan. Use it in an early stage of the game loader.
Look at the examples provided.
GoECS() is just a call to ResetDisplay() followed by a call to ResetSprites().
See also: ResetDisplay(), ResetSprites()
JOTDStartup/ResetDisplay()
void ResetDisplay(void) - Resets 15KHz display
This function avoids to get a modulo/AGA display problem.
NOTE: This function will have no effect if the system copperlists are still active,
as they reset BPLCON3 every screen scan. Use it in an early stage of the game loader.
Look at the examples provided.
See also: GoECS(), ResetSprites()
JOTDStartup/ResetSprites()
void ResetSprites(void) - Resets LORES sprites
If the sprites are too small vertically, call this function.
NOTE: This function will have no effect if the system copperlists are still active,
as they reset BPLCON3 every screen scan. Use it in an early stage of the game loader.
Look at the examples provided.
See also: GoECS(), ResetDisplay()
JOTDStartup/KickVerTest()
ULONG KickVerTest(kickversion) - Test if ROM version is newer than a given version
D0 D0
KickVerTest() uses the SoftVer variable in the ExecBase structure to test the
kickstart version.
kickversion is the kickstart version number (for instance 35,36,39...)
The function will return 0 is the installed version is newer than the wanted version
and -1 if not.
See also: Kick37Test()
JOTDStartup/Kick37Test()
ULONG Kick37Test(void) - Test if ROM is V37 or newer
D0
The same thing as KickVerTest(), but compares the version to V37 (matches Workbench 2.0).
Useful when you don't know if you can use 2.0 features.
Returns D0=0 if KS>36, D0!=0 if KS<=36
See also: KickVerTest()
JOTDStartup/InitTrackDisk()
void InitTrackDisk(void) - Initialize trackdisk.device emulation
Call this when you need to emulate a DoIO(), very frequent in the bootblock of
games, which use trackdisk.device to load their loading routines, then forget
it along with the remainder of the system.
When you re-source the bootblock, replace all the JSRLIB DoIO by JSRGEN TrackLoadFast.
Before jumping to the bootblock code, you MUST call InitTrackDisk (use JSRGEN too),
or else the loads will be done anywhere in the memory -> GURU.
Refer to the examples for more details.
See also: TrackLoadFast(),SetTDUnit(),ReadRobSectorsFast()
JOTDStartup/TrackLoad
void TrackLoad(void) - Replaces DoIO function to read from a virtual floppy
This function replaces TrackLoadFast. It has the same function, but is able to
read from HD when LOWMEM is activated.
This function replaces the DoIO() function in the case of a floppy disk.
Once initialized with InitTrackDisk() and possibly with SetTDUnit(), you are
able to read sectors from the virtual floppy device exactly with the trackdisk.device
(only read is supported. There is no write mode).
See also: InitTrackDisk(),SetTDUnit(),ReadRobSectors()
JOTDStartup/SetTDUnit()
void SetTDUnit(unit) - Sets trackdisk.device emulation unit
D0
This function allows to change the current virtual drive (actually it changes disks).
Use it in conjunction with TrackLoadFast(). It has no effect with ReadRobSectorsFast().
The unit is not limited to 3, as you can patch games which have got 4 floppies or more.
If this routine is not called, the unit will be 0 (default, useful for bootblocks).
See also: InitTrackDisk(),TrackLoad()
JOTDStartup/ReadFileFast()
ULONG ReadFileFast(filename,buffer,command,length) - Transfer a file in the given buffer
D0 A0 A1 D0 D1
Uses the Rob Northern file interface.
used in:
Cannon Fodder 2, Darkmere, Sensible Golf, Sensible Soccer, Road Rash...
Different versions may exist: D1 can be meaningless. In this case, set it to -1.
Some games only use commands 0 and 5
Darkmere uses 0, 5, 6, 7, 8 extensively.
in:
A0: Filename
A1: Buffer
D0: command:
D1: misc
D0:
0: Read
in : D1: # bytes (-1: all), A0: filename, A1: buffer
out: D0: 0 if OK, D1: length read
1: Write (disabled)
in: D1: # bytes , A0: filename, A1: buffer
out: D0: 0 if OK, D1: length written
2: Never seen it before ????
3: Read directory (disabled) A0:
4: Format floppy (disabled)
5: Get Length
in : A0: filename
out: D0: 1 if found, 0 else, D1: length
6: Nothing (I think)
7: Read last file w/ offset
in : D1: # bytes , A0: scratch ,A1: buffer
out: D0: ???
8: Set offset on last accessed file
in : D1: offset , A0: scratch ,A1: scratch
out: D0: offset
In order to use this function, you must initialize the fastfile structure by calling
LoadFiles(), else this will fail.
filename: a NULL-terminated string
buffer: a pointer on a memory zone
length: the length in bytes. If you pass -1, the whole file will be read.
Sometimes, the games use this routine as-is, and you've got only to patch.
However, you've got to check for files which are not found: they can be savegames loads.
In that case, you can call the original routine (read from floppy) or make a HD access
using InGameOSCall() (much harder, provided you've got to read the directory)
To read part of files (using the 7 and 8 commands), it's better to use ReadFilePart()
because the interface is more natural. However, this routine can be used as-is in some
games (such as Darkmere, which uses the 7 and 8 commands, and took me a while to figure
it out!)
See also: LoadFiles(),ReadFile(),ReadFilePart()
JOTDStartup/ReadRobSectors()
ULONG ReadRobSectors(drivenum,offset,blocks,command,buffer) - Reads sectors from virtual disk
D0 D0 D1 D2 D3 A0
This is the main all-purpose loading routine of the package. I chose this syntax
because this routine is used as-is in lots of games and can be used provided
an offset correction or a disk choice is done in many games.
I noticed it and replaced it in:
Assassin, BodyBlows, AlienBreed, Warzone, Magic Pockets, Cadaver,
Chaos Engine, Cannon Fodder 2, Project-X, Mortal Kombat I & II,
Gods, Rodland, Desert Strike, Qwak, and others...
INTERFACE:
drivenum/D0 = Drive Number - sometimes matches disk number
offset/D1 = Offset in bytes / 512 (also offset in sectors)
block/D2 = nb of 512 bytes sectors to read
command/D3 = 0: Read data, !=0 Writes (has to be done manually, as it's better to create a separate file for game saves)
buffer/A0 = Start address
For 2 disked games using DF0: and DF1:, you generally don't have to worry about disk changes,
as disk1 is supposed to be in DF0: and disk2 in DF1:, so D0 will be set in a correct way and
the game will read from the correct disk anyway.
See the examples to understand fully the interest of this routine.
Specifying the LOWMEM tooltype/argument at startup causes this
routine to read the data from the file instead of the cached images
in memory (which will not exist if LOWMEM is activated).
See also: TrackLoadFast(), LoadDisks(), ReadDiskPart()
JOTDStartup/ReadDiskPart()
ULONG ReadDiskPart(drivenum,length,offset,buffer) - Reads bytes from virtual disk
D0 D0 D1 D2 A0
Even if the Rob Northen format is widespread, this routine is the alternative.
It is able to read any number of bytes and at any location on a disk image. It's useful
for disk loaders which use a non $200 round interface.
INTERFACE:
drivenum/D0 = Drive Number - sometimes matches disk number
offset/D1 = Length in bytes to read
block/D2 = Offset in bytes from where to read
buffer/A0 = Start address
Specifying the LOWMEM tooltype/argument at startup causes this
routine to read the data from the file instead of the cached images
in memory (which will not exist if LOWMEM is activated).
See also: TrackLoadFast(), LoadDisks(), ReadRobSectors()
JOTDStartup/StrcpyAsm()
void StrcpyAsm(string1,string2) - Copies string
D0 D1
Copies the string pointed by D0 to the buffer area in D1.
The source string must be NULL terminated.
See also: StrcmpAsm(), StrlenAsm(), ToUpper()
JOTDStartup/StrcmpAsm()
LONG StrcmpAsm(string1,string2) - Compares 2 strings (not case sensitive)
D0 D0 D1
StrcmpAsm returns 0 if the 2 strings are the same (no case sensitivity,
e.g. LoA-Der is the same as LOa-deR), and -1 if they're different
See also: StrcmpAsm(), StrcpyAsm(), ToUpper()
JOTDStartup/StrlenAsm()
ULONG StrlenAsm(string) - Returns the length of a NULL-terminated string
D0 D0
JOTDStartup/ToUpperAsm()
void ToUpperAsm(string) - Converts a string in upper case
D0 D0
JOTDStartup/HexReplaceLong()
void HexReplaceLong(searched,replacer,start,end) - Searches a longword and replaces it
D0 D1 A0 A1
Designed for automatic search/replace of data/code.
This function can be useful to search blitter commands (move.w Dx,($58,Ax)) to patch
them with WaitBlit. For example, I'd like to insert blitterwaits in the zone
$1000-$5000 for the instruction move.w D1,($58,A2) (hex: $35410058)
...
move.l #$35410058,D0
move.l #$4EB800C6,D1 ; JSR ($C6).W
lea $1000.W,A0
lea $5000.W,A1
JSRGEN HexReplaceLong
PATCHUSRJMP $C6.W,DoBlit_D1A2
...
DoBlit_D1A2:
JSRGEN WaitBlit ; wait blitter operations to complete
move.w D1,($58,A2) ; start the blit
rts
The search will be done word by word (2 bytes). Odd occurences will not be found.
See also: HexReplaceWord
JOTDStartup/HexReplaceWord()
void HexReplaceWord(searched,replacer,start,end) - Searches a word and replaces it
D0.W D1.W A0 A1
Designed for automatic search/replace of data/code.
Same use as HexReplaceLong().
The search will be done word by word (2 bytes). Odd occurences will not be found.
See also: HexReplaceLong()
JOTDStartup/SetFakeFunction()
void SetFakeFunction(offset, new function) - Sets user defined function in fakeexec table
D0 A0
The exec library emulation is poor. You'll have to define your own functions to make
it better (game specific or not).
For instance, if a game uses AllocMem(), it's up
to you to choose what to return, to keep track of the allocated blocks or not
(is it worth? is AllocMem() called more than once, twice...?)
You could do it in a "dirty" way, by getting FakeExecBase in $4 and patching the offset,
but I created a function for this. As a bonus, the function will check:
1. That you pass an offset which is between 0 and -$300
2. That OpenFakeExec() was called (before SaveOSData)
If both conditions are not passed, the function will trigger an explicit run-time error.
Example: define your own AvailMem()
...
lea MyOwn_AvailMem(pc),A0
GETLVO AvailMem
JSRGEN SetFakeFunction
...
MyOwn_AvailMem:
move.l #$70000,D0
rts
I used my new macro GETLVO to get an LVO in D0. (move.l #_LVO1,D0)
See also: OpenFakeExec()
JOTDStartup/CRC16()
UWORD CRC16 (buffer, length) - Calculates CRC16 checksum
D0 A0 D0
This routine calculates CRC16 checksum of a block according to the
ANSI CRC16 algorithm.
Thanks to Andreas Kleiner for putting the crc.c source on aminet,
which I converted into assembler (available on request)
This routine was added because of the WHDLoad slave emulation.
JOTDStartup/HexToString()
void HexToString(number,buffer) - Converts a hex number to a ascii string
D0 A1
The buffer has to be at least as wide as 10 chars. The format will always be:
$xxxxxxxx.
e.g: D0=$37 gives A1-> $00000037
D0=$DEADBEEF gives A1-> $DEADBEEF
JOTDStartup/Save & Restore hardware registers
void SaveCustomRegs() - Save readable custom registers
void RestoreCustomRegs() - Restore previously saved custom registers
void SaveCIARegs() - Save CIA registers
void RestoreCIARegs() - Restore CIA registers
Those functions are used internally to save/restore intena, intreq, adkcon
dmacon, and the CIA registers from/to the operating system.
So the game can (and will) trash those registers, but they will be restored
when InGameExit() is called.
You should not use those functions, unless you know what you are doing.
See also: InGameExit(), SaveOSData(), SAVE_OSDATA (macro)
JOTDStartup/RNCLength()
ULONG RNCLength(memory) - returns the length of a decrunched RNC file
D0 A0
This function returns 0 if the zone pointed by A0 is not a RNC crunched file.
Else, it will return the number of bytes the file will take once decrunched.
See also - LoadRNCFile(), RNCDecrunch()
JOTDStartup/RNCDecrunch()
ULONG RNCDecrunch(crunchbuffer,decrunchbuffer) - Decrunches a RNC type 1 file
D0 A0 A1
This routine handles Rob Northen Cruncher files (header: RNC01).
This type of cruncher is very heavily used in lots of games
from EOA, Team 17, Renegade, Akklaim, and lots of others.
A0 points on the crunched buffer start (the file just loaded), and
A1 points on the destination.
This routine returns 0 if an error occured.
The two addresses may be the same, as the decrunch algorithm is able to
overwrite the crunched data during decrunch.
Be careful to allocate or reserve enough memory to decrunch the file.
Use the RNCLength() function to know the length of the file once decrunched.
LoadRNCFile uses this routine, but RNCDecrunch can be useful when the game is NONDOS
and the data is trackloaded.
NOTE: RNC crunched files are handled by the XFD library (decrunch only)
The cruncher (ProPack) is not freely distributable, and reserved to
game programmers.
See also - LoadRNCFile(), RNCLength(), RNCDecrunchEncrypted(), ATNDecrunch()
JOTDStartup/RNCDecrunchEncrypted()
ULONG RNCDecrunchEncrypted(key,crunchbuffer,decrunchbuffer) - Decrunches a RNC type 1 file, with encryption
D0 D0 A0 A1
Some games, like Walker, use RNC compression, but encrypt data with a 16/32
bit key. You'll need to know the key to be able to decrunch the file
properly. In most cases, leave the game handle that. I use this routine
because in most games the decrunch routines are located in chipmem and it's
slow. You can patch the entry of a RNC decrunch routine by this call,
which is in fastmem, and you'll be amazed by the speed.
See also - LoadRNCFile(), RNCLength(), RNCDecrunch(), ATNDecrunch(), PPDecrunch(),
FungusDecrunch()
JOTDStartup/ATNDecrunch()
ULONG ATNDecrunch(crunchbuffer,decrunchbuffer) - Decrunches a ATN! file
D0 A0 A1
This routine handles ATN files (header: ATN!).
This type of cruncher is very heavily used in lots of games
from Team 17 (Arcade Pool, Project-X...)
A0 points on the crunched buffer start (the file just loaded), and
A1 points on the destination.
This routine returns 0 if an error occured.
The two addresses may be the same, as the decrunch algorithm is able to
overwrite the crunched data during decrunch.
Be careful to allocate or reserve enough memory to decrunch the file.
Use the ATNLength() function to know the length of the file once decrunched.
NOTE: ATN crunched files are handled by the XFD library (decrunch only)
The cruncher is not available. This cruncher no longer appears in
Team 17 games and has been replaced by RNC.
See also - LoadRNCFile(), RNCLength(), RNCDecrunchEncrypted()
JOTDStartup/PPDecrunch()
PPDecrunch(end_crunchbuf,start_crunchbuf,decrunchbuf) - Decrunches a PP20 file
A0 A1 A2
As I saw this routine in at least one game and I know PowerPacker
by Nico François is very popular on the amiga, I included this decrunch routine.
This applies to file beginning by the PP20 header (PowerPacker 2.0)
in: A0 end of source buffer
A1 start of dest buffer
A2 start of source buffer
out: nothing, but the buffer is decrunched :-)
All registers are preserved in this call.
See also - RNCDecrunch(), ATNDecrunch(), FungusDecrunch()
JOTDStartup/FungusDecrunch()
void FungusDecrunch(crunchbuf,decrunchbuf) - Decrunches a FUNGUS file
A0 A1
The FUNGUS packer (maybe also called SF or SA packer) is mostly used
in Gremlins games (Zool, Switchblade 2...)
in: A0: crunched buffer start
in: A1: destination (may be the same, like in RNC decruncher)
CAUTION: This decrunch routine does not check that the data is correct.
Unreliable results may occur if you try to decrunch a random block of memory.
See also - RNCDecrunch(), ATNDecrunch(), PPDecrunch()
JOTDStartup/BlackScreen()
void BlackScreen() - Sets all the colors to black
This function will not build a copperlist. It will just clear color registers.
JOTDStartup/EnterDebugger()
void EnterDebugger(void) - Enters debugger if one is installer
ATM only HRTMon is property supported. A version of HRTMon which works is for instance
2.22. All versions above this one work too.
EnterDebugger calls the debugger just as if you put a breakpoint there, except that
you don't have too :)
VERY useful function when the loader is in development phase, to remove afterwards!
This function will do nothing if no debugger was found. Run JST with VERBOSE or TEST on
to see which debugger is currently loaded.
See also:
JOTDStartup/SetTraceVector()
APTR SetTraceVector(APTR trace_entrypoint) - sets trace vector to a user routine
A0 A0
Now that JST relocates the VBR (unless you use the VBR-specific tooltypes), it's no
longer possible to poke directly in the trace exception vector to install a tracer
by poking in $24, since JST will intercept the trace exception with the relocated VBR.
This function allows to do it transparently.
in: A0 points to your trace code. Of course it's up to you to preserve registers on exit.
Exit by RTE (not RTS)
out: A0 points to the old trace code. Most of the time it's the routine JST installed.
See also:
JOTDStartup/WaitMouse()
void WaitMouse(void) - Waits for LMB while the screen is full of colors
Useful to see if a point is reached. Waits until the LMB is pressed to exit.
See also: WaitMouseInterrupt()
JOTDStartup/WaitMouseInterrupt()
void WaitMouseInterrupt(void) - Waits for LMB while the screen is full of colors, interrupts active
Same routine as WaitMouse() except that as a bonus, interrupts will be enabled during the
wait, allowing you to break with a software debugger like HRTMon.
But this function can lead to crashes in some cases (e.g. the interrupts were disabled
because the interrupts vectors were not set by the game yet)
See also: WaitMouse()
JOTDStartup/InGameExit()
void InGameExit() - returns to the OS while the game is running
This function will return with a rts if no memory was available
for the chipmem (SaveOSData()). The best way to call it is from
a JSRGEN, then return to the program if it could not quit.
This function was moved from the absolute part to the relocatable part
in order to avoid crashes when the absolute program has be overridden
by the game. JSRABS InGameExit will not work (it will not crash in
most cases but will do nothing).
Now you can call this function before having called SaveOSData()
or the SAVE_OSDATA macro from the user program.
Call it whenever you want during the game, even if the OS
is totally killed. This function will resurrect the OS and will
call the user routine you specified using the SetExitRoutine function
if any. (see macro HDPARAMS)
This function has be greatly improved. It succeeds on 99.9% of games.
See also: SaveOSData(), InGameOSCall()
JOTDStartup/IsRegistered()
ULONG IsRegistered(void) - Check if the current version of JST is registered
D0
Returns 0 in D0 if the version of JST currently used is not registered
Returns 1 in D0 if the version of JST currently used is registered (from v1.1e)
See also:
JOTDStartup/SetExitRoutine()
void SetExitRoutine(routine entrypoint) - calls a user routine on exit
A0
This function will allow the user to specify a function to call
when the OS has been restored with InGameExit(). For instance, this function
can save scores to a file without the use of WriteFileHD but in a normal DOS
way, or can display a message, free some manually allocated memory...
If you want to save scores on exit which are located in chip memory,
remember that you'll have to copy the scores in fastmem before calling
InGameExit, or the buffer will be trashed by the OS chipmem.
The user routine must end by RTS.
CAUTION: The input was D0 and now it's A0 because it's more convenient for
relocatable code.
See also: InGameExit()
JOTDStartup/InGameIconify()
void InGameIconify() - returns to the OS while the game is running, with possibility of return
This function acts exactly like InGameExit BUT it allows to return to the game!
That seems impossible to do because of the read only registers, that's why some special
functions are to be applied on the game before you can use this function.
Actually, JST is already able to swap the OS but the display, because the copper pointer
is ready only.
But you can use some other functions to patch the game at some strategic locations
when it stores the copperlist pointer into the copper pointer register (COP1LC, $80).
Those functions modify the MOVE instruction which are used to store the
copperlist pointer very easily.
Copperlist pointer store can be of these types:
1: move.l Ax,($80,Ay)
2: move.l #chipaddr,($80,Ay)
3: move.l addr,($80,Ay)
4: move.l #chipaddr,$DFF080
5: move.l addr,$DFF080
6: move.l Ax,$DFF080
There are many ways to store a copperlist pointer, but those are the most frequent occurences.
--
CASE 1:
To search (and patch) case 1, you can use HexReplaceLong() because this instruction is
4 bytes long and is totally determined once you found which registers the game is using
(with HRTMon just search with fi and the pattern A*,($80,A*) )
Replace the instruction by $4EB8addr where $addr is a zero page address where you can
put the storage code or put a PATCHUSRJMP to a user routine (I prefer that second solution)
Be careful of the range, and check the locations you patch are actually copper moves
(just ensure Ay contains $DFF000), or else the iconify could go bezerk
(but the game will still work)
In that use routine, you'll put the value in the copper pointer and register it to JST
using the SetCopperPointer() function. You've done it.
This is the most difficult and manual case, but when you're used to it, that's not a concern
anymore.
CASE 2:
You lucky guys I made a special search and patch routine for those frequent occurences.
It's called PatchMoveCList_Idx(). There are a couple of parameters
but it's very easy to use and totally transparent.
You've got to put it at the right place, that's all.
CASE 3:
Like in case 2, I also made a routine called PatchMoveCList_Abs()
CASE 4:
Like in case 2, I also made a routine called PatchMoveCList_Ind()
CASE 5:
No routine implemented in JST. Totally manual method. I may add a PatchMoveCList_xxx function
later but now I don't feel like doing it.
CASE 6:
Well, totally manual method, but easy. Note down the addresses, and then make a PATCHUSRJSR
to your user routine (the instruction is also 6 bytes long)
which will do the poke in the copperlist register and will log
the copper pointer value to JST using SetCopperPointer().
--
If you cannot find in copperlist store instruction belonging to those 6 cases above,
you'll have to search further, but game coders usually don't hide this kind
of instruction too much (it can happen, though, when sometimes they try to fool
the Action Replay cartridges).
You've got another solution if you don't find the copperlist pointer: look into the game
using Action Replay cartridge (or equivalent) or do a copper search with HTRMon.
If the copperlist location does not move, you can hardcode it in your program and call
SetCopperPointer() with this address.
When you logged all the copperlist pointer store instructions, InGameIconify can be called
exactly like InGameExit, using for instance the TAB key in the keyboard interrupt.
In some cases, you'll have to set some hardware registers 'by hand' after JST resumes the game.
For instance, I had to put a move.w #$400F,fmode+$DFF000 in Street Racer, or else the display
was trashed.
But in some games like Menace or Lotus, the iconification works perfectly.
InGameIconify won't do anything if the current copperlist pointer is set to 0.
You can set it to this value with StoreCopperPointer()
See the examples (Menace, Street Racer, Lotus Turbo Challenge) to figure out how to do
this.
Warning: QUIET will prevent this function to work. It will do nothing in that case. Just
because JST uses the console in the iconify menu.
See also: InGameExit(), StoreCopperPointer(), TellCopperPointer(), PatchMoveCList_Idx(),
PatchMoveCList_Abs(), HexReplaceLong()
JOTDStartup/PatchMoveCList_Abs()
void PatchMoveCList_Abs(start, end, trap_number) - patches copper list stores: move.l #adr,$DFF080
A0 A1 D1
This function puts a TRAP when it finds a MOVE.L #ADR,$DFF080 instruction.
ADR is checked so no patch is done if ADR is not in chipmem.
If D1 is out of $0-$F range, this function will do nothing. You can usually choose any of those
numbers (which match trap numbers location $80 ->$BC), but some games use some traps, and
obviously you cannot use the same ones. You'd be very unlucky if the game used ALL the traps.
In that case, you've got to patch manually.
A0: start address: MUST BE AN EVEN ADDRESS!
A1: end address
D1: trap number ($0-$F)
This function flushes the caches at the end of the operation.
See also: PatchMoveCList_Idx(), PatchMoveCList_Ind(), InGameIconify()
JOTDStartup/PatchMoveCList_Ind()
void PatchMoveCList_Ind(start, end, trap_number) - patches copper list stores: move.l adr,$DFF080
A0 A1 D1
This function puts a TRAP when it finds a MOVE.L ADR,$DFF080 instruction.
ADR is checked so no patch is done if ADR is not in chipmem.
If D1 is out of $0-$F range, this function will do nothing. You can usually choose any of those
numbers (which match trap numbers location $80 ->$BC), but some games use some traps, and
obviously you cannot use the same ones. You'd be very unlucky if the game used ALL the traps.
In that case, you've got to patch manually.
A0: start address: MUST BE AN EVEN ADDRESS!
A1: end address
D1: trap number ($0-$F)
This function flushes the caches at the end of the operation.
See also: PatchMoveCList_Idx(), PatchMoveCList_Abs(), InGameIconify()
JOTDStartup/PatchMoveCList_Idx()
void PatchMoveCList_Idx(start, end, register#, trap#) - patches copper list stores: move.l #adr,($80,Ax)
A0 A1 D0 D1
This function puts a TRAP when it finds a MOVE.L #ADR,($80,Ax) instruction if the
register number you specified matches Ax. It stores the copper pointer so InGameIconify
knows where it is.
ADR is checked so no patch is done if ADR is not in chipmem.
This function will do nothing if D1 is out of $0-$F range or if D0 is out of 0-6 range.
Most of the time, you can choose any of those numbers $0-$F for D1
(which match trap numbers location $80 ->$BC) but some games use some traps, and
obviously you cannot use the same ones. You'd be very unlucky if the game used ALL the traps.
In that case, you've got to patch manually (hard luck!)
A0: start address: MUST BE AN EVEN ADDRESS!
A1: end address
D0: register number (0-6)
D1: trap number ($0-$F)
This function flushes the caches at the end of the operation.
See also: PatchMoveCList_Abs(), PatchMoveCList_Ind(), InGameIconify()
JOTDStartup/PatchMoveBlit_Idx()
void PatchMoveBlit_Idx(start, end, register#, trap#) - patches blit moves: move.w #adr,($58,Ax)
A0 A1 D0 D1
This function puts a TRAP when it finds a MOVE.W #ADR,($58,Ax) instruction if the
register number you specified matches Ax.
The trap performs the blit but calls WaitBlit() before, so the
blitter is always ready and there are no blitter errors due to those calls.
This function will do nothing if D1 is out of $0-$F range or if D0 is out of 0-6 range.
Most of the time, you can choose any of those numbers $0-$F for D1
(which match trap numbers location $80 ->$BC) but some games use some traps, and
obviously you cannot use the same ones. You'd be very unlucky if the game used ALL the traps.
In that case, you've got to patch manually (hard luck!)
A0: start address: MUST BE AN EVEN ADDRESS!
A1: end address
D0: register number (0-6)
D1: trap number ($0-$F)
This function flushes the caches at the end of the operation.
See also: WaitBlit()
JOTDStartup/StoreCopperPointer()
void StoreCopperPointer(cptr) - Logs the current (believed) copperlist pointer to JST
D0
D0 can be guessed or read from an instruction in the game.
Passing -1.L to this function will disable Iconification (JST believes the copperlist
is not known anymore)
See also: InGameIconify(), TellCopperPointer(), PatchMoveCList_Abs(), PatchMoveCList_Idx()
JOTDStartup/TellCopperPointer()
ULONG TellCopperPointer(void) - Returns the current logged copperlist pointer
D0
Gets current copperlist pointer if you had logged the copperlist moves.
This function will return -1.L if the copperlist has not been logged yet.
See also: InGameIconify(), TellCopperPointer(), PatchMoveCList_Abs(), PatchMoveCList_Idx()
JOTDStartup/BeamDelay()
void BeamDelay(beamticks) - waits using VPOSR register
D0.W
This function will wait approx. D0*20ms.
Use it to fix some CPU dependent loops like
loop
dbf Dx,loop
by dividing Dx by #$28 and calling BeamDelay
with value of Dx in D0
(problem mentionned by Harry in some Soundtracker replay routines)
move.w Dx,D0
divu #$28,D0
swap D0
clr.w D0
swap D0
JSRGEN BeamDelay
(or BEAM_DELAY D0)
See also: BEAM_DELAY (macro)
JOTDStartup/WaitBlit()
void WaitBlit(void) - waits for blitter operations to complete
This small routine, inserted at the proper location, allows to avoid
blitter gfx bugs in loads of games (DoodleBug, Premiere, James Pond,
Ninja Spirit...)
and avoid crashes due to those blitter bugs (Lotus I, Lotus III, X-Out...)
You can either insert it before the blit (better) or after the blit
(safer, but can slowdown the game)
A blit is a write access to register $DFF058. See the JOTDHDInstall.guide
to learn how to find those faulty blits.
See also: WAIT_BLIT (macro)
JOTDStartup/SetQuitKey()
void SetQuitKey(key,user_routine) - Activates auto quit key
D0 A0
D0: raw keycode
A0: user routine to be called before InGameExit
If A0=0, then no user routine will be called (default exit)
Note: Ralf prefered this feature to be reserved to registered users only. That's his
will, and since he coded the stuff, I only have to agree.
If QUITKEY tooltype is set, SetQuitKey will use the QUITKEY tooltype value instead of
the loader built-in value.
See also: SAVEOS_DATA (macro), SetIconifyKey()
JOTDStartup/SetIconifyKey()
ULONG SetIconifyKey(key,user_routine) - Activates auto iconify key
D0 D0 A0
D0: raw keycode (if D0 is 0 no iconify will be attempted)
A0: custom code which will be called:
*before* InGameIconify with D0 = 0
*after* InGameIconify with D0 = 1
If A0=0, then no user routine will be called.
If the Custom code returns with D0 != 0 no iconify will be performed
Remember that installing a iconify feature on a loader is tricky.:)
Note: Ralf prefered this feature to be reserved to registered users only. That's his
will, and since he coded the stuff, I only have to agree. If used with a non-registered
JST, this routine will do nothing.
If QUITKEY tooltype is set, SetQuitKey will use the QUITKEY tooltype value instead of
the loader built-in value.
See also: InGameIconify(), SetQuitKey()
JOTDStartup/LogPatch()
void LogPatch(address,length) - tells JST the patches you're going to do
A0 D0
PATCHUSRJSR, PATCHUSRJMP, PATCH_RTS, PATCHGENJSR and PATCHGENJMP include this function
automatically if PATCH_LOGGED variable is set.
LogPatch saves D0 bytes of the memory at location A0. Useful to save the bytes you
patch upon.
The log file is called "patch.log". It is saved on exit in the current directory
or the directory specified by SAVEDIR. This logfile can be analysed using the
printlog tool.
See also: InitLogPatch, REGISTER_PATCH (macro)
JOTDStartup/InitLogPatch()
void InitLogPatch(void) - Initializes patch logging
Internal use. Called if the PATCH_LOGGED constant is defined in your loader BEFORE
the jst.i include.
See also: LogPatch, REGISTER_PATCH (macro)
JOTDStartup/The printlog tool
This tool is used to turn the binary patch.log files into viewable text.
This is very useful to update loaders to newer versions, which are generally
slightly different than the one you're already done...
Usage:
printlog <logfile> [<sourcefile>] [NOZPAGE]
logfile (compulsory): the logfile generated with JST (patch.log)
sourcefile (optionnal): put your asm source file here. When a patch address is found,
printlog makes a "grep" in your sourcefile and displays the line used to patch this location
of course your sourcefile is NEVER written into.
NOZPAGE: does not show zero page patches matches in the sourcefile (if you patched in $D0, you
can have a trifle of lines matched :) )
Redirect the output to a file to see the patches you applied.
See also: LogPatch(), REGISTER_PATCH